import java.util.*;

// 论文选择策略接口
interface PaperSelectionStrategy {
    List<Paper> selectPapers(List<Paper> availablePapers, int quantity);
}

// 随机选择策略
class RandomStrategy implements PaperSelectionStrategy {
    @Override
    public List<Paper> selectPapers(List<Paper> availablePapers, int quantity) {
        List<Paper> result = new ArrayList<>(availablePapers);
        Collections.shuffle(result);
        return result.subList(0, Math.min(quantity, result.size()));
    }
}

// 顺序选择策略（按论文加入列表的次序从早到晚来选）
class SequentialStrategy implements PaperSelectionStrategy {
    @Override
    public List<Paper> selectPapers(List<Paper> availablePapers, int quantity) {
        return availablePapers.subList(0, Math.min(quantity, availablePapers.size()));
    }
}

// 自定义异常类
class NoEnoughPapersException extends Exception {
    public NoEnoughPapersException(String message) {
        super(message);
    }
}

// 论文类型枚举
enum PaperType {
    Thesis,
    ConferencePaper,
    JournalPaper,
    Any
}

// 论文基类
abstract class Paper {
    private final String title;

    public Paper(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
}

// 具体论文类型
class Thesis extends Paper {
    public Thesis(String title) {
        super(title);
    }
}

class JournalPaper extends Paper {
    public JournalPaper(String title) {
        super(title);
    }
}

class ConferencePaper extends Paper {
    public ConferencePaper(String title) {
        super(title);
    }
}

// 学生类
class Student {
    private final String name;
    private final int preferredQuantity;
    private final PaperType preferredType;

    public Student(String name, int quantity, PaperType type) {
        this.name = name;
        this.preferredQuantity = quantity;
        this.preferredType = type;
    }

    public int getPreferredQuantity() {
        return preferredQuantity;
    }

    public PaperType getPreferredPaperType() {
        return preferredType;
    }

    public String getName() {
        return name;
    }
}

// 论文选择记录类
class PaperSelection {
    private final Map<Paper, Student> selections = new HashMap<>();

    public boolean addSelection(Student student, Paper paper) {
        selections.put(paper, student);
        return true;
    }

    public boolean isSelected(Paper paper) {
        return selections.containsKey(paper);
    }

    public Map<Paper, Student> getSelections() {
        return Collections.unmodifiableMap(selections);
    }

    @Override
    public String toString() {
        StringBuilder str = new StringBuilder();
        for (Map.Entry<Paper, Student> entry : selections.entrySet()) {
            str.append(entry.getKey().getTitle()).append("\t selected by ").append(entry.getValue().getName()).append("\n");
        }
        return str.toString();
    }
}

// 论文列表类
class PaperList extends PaperSelection {
    private final String teacher;
    private final List<Paper> papers;
    private final PaperSelection selections = new PaperSelection();
    private PaperSelectionStrategy strategy;

    public PaperList(String teacher, List<Paper> papers) {
        this.teacher = teacher;
        this.papers = papers;
        this.strategy = new RandomStrategy(); // 默认策略
    }

    public void setStrategy(PaperSelectionStrategy strategy) {
        this.strategy = strategy;
    }

    private List<Paper> getUnselectedPapersByType(PaperType type) {
        List<Paper> results = new ArrayList<>();
        for (Paper p : papers) {
            if (!selections.isSelected(p)) {
                if (type == PaperType.Any || p.getClass().equals(getPaperClassForType(type))) {
                    results.add(p);
                }
            }
        }
        return results;
    }

    private Class<? extends Paper> getPaperClassForType(PaperType type) {
        switch (type) {
            case Thesis: return Thesis.class;
            case ConferencePaper: return ConferencePaper.class;
            case JournalPaper: return JournalPaper.class;
            default: return Paper.class; // 不会执行到，因为Any情况在getUnselectedPapersByType中单独处理
        }
    }

    public List<Paper> selectPaper(Student student) throws NoEnoughPapersException {
        List<Paper> availablePapers = getUnselectedPapersByType(student.getPreferredPaperType());
        if (availablePapers.size() < student.getPreferredQuantity()) {
            throw new NoEnoughPapersException("学生 " + student.getName() + " 希望选论文 " + student.getPreferredQuantity() + " 篇，但目前只有 " + availablePapers.size() + " 篇可选");
        }

        List<Paper> selectedPapers = strategy.selectPapers(availablePapers, student.getPreferredQuantity());
        for (Paper p : selectedPapers) {
            selections.addSelection(student, p);
        }

        return selectedPapers;
    }

    public PaperSelection getSelectionResult() {
        return selections;
    }
}

// 客户端测试类
public class Client {
    public static void main(String[] args) {
        List<Paper> papers = Arrays.asList(
                new Thesis("Science Thesis"),
                new ConferencePaper("Science Conference"),
                new JournalPaper("Technology Journal")
        );

        PaperList paperList = new PaperList("Rainy", papers);
        Student student = new Student("Cloudy", 2, PaperType.Any);

        // 使用随机策略
        paperList.setStrategy(new RandomStrategy());
        try {
            List<Paper> selectedPapers = paperList.selectPaper(student);
            System.out.println("随机策略选择结果:");
            for (Paper paper : selectedPapers) {
                System.out.println(paper.getTitle());
            }
        } catch (NoEnoughPapersException e) {
            System.out.println(e.getMessage());
        }

        // 使用顺序策略
        paperList.setStrategy(new SequentialStrategy());
        try {
            List<Paper> selectedPapers = paperList.selectPaper(student);
            System.out.println("顺序策略选择结果:");
            for (Paper paper : selectedPapers) {
                System.out.println(paper.getTitle());
            }
        } catch (NoEnoughPapersException e) {
            System.out.println(e.getMessage());
        }
    }
}